﻿namespace WebPacker.Configuration
{
	using System;
	using System.Collections.Generic;
	using System.IO;
	using System.Xml;
	using NXml.Serialization;
	using WebPacker.Filters;
	using WebPacker.Logger;
	using WebPacker.Packs.Builders;
	using WebPacker.Packs.Watchers;
	using WebPacker.Providers;
	using WebPacker.Repositories;
	using WebPacker.VersionGenerators;

	public class WebPackerConfiguration
	{
		private static readonly IVersionGenerator[] defaultVersionGenerators;

		static WebPackerConfiguration()
		{
			defaultVersionGenerators = new IVersionGenerator[]
			{
				new ManualVersionGenerator(),
				new Md5VersionGenerator(),
				new LastModifiedVersionGenerator(),
				new HashCodeVersionGenerator()
			};
		}

		public WebPackerConfiguration()
		{
			Enabled = true;
			Compression = false;
			MonitoringDelay = 1;
		}

		public bool Enabled { get; set; }

		public bool ThrowsExceptions { get; set; }

		public int MonitoringDelay { get; set; }

		public bool Compression { get; set; }

		public IPackProvider Provider { get; set; }

		public IFilter[] Filters { get; set; }

		public ILogger Logger { get; set; }

		public IVersionGenerator[] VersionGenerators { get; set; }

		public PackInfoCollection Packs { get; set; }

		public IPackRouter CreateRouter()
		{
			if (Logger != null)
			{
				LogFactory.SetLogger(Logger);
			}

			CompositeWatcher watcher = null;
			PackRepository packRepository = null;

			try
			{
				var generators = new List<IVersionGenerator>();

				if (VersionGenerators != null)
				{
					generators.AddRange(VersionGenerators);
				}

				foreach (var generator in defaultVersionGenerators)
				{
					if (generators.Find(x => x.Name == generator.Name) == null)
					{
						generators.Add(generator);
					}
				}

				watcher = new CompositeWatcher
				{
					NotifyDelay = MonitoringDelay * 1000
				};

				watcher.Register(new FileWatcher());
				watcher.Register(new RemoteWatcher());

				var packBuilder = new PackBuilder(Filters, watcher, generators);
				packRepository = new PackRepository(packBuilder);

				if (!string.IsNullOrEmpty(Packs.Source))
				{
					packRepository.Source = Packs.Source;
				}
				else
				{
					packRepository.SetPacks(Packs);
				}

				return new WebPackerRouter(packRepository, Provider)
				{
					Enabled = Enabled,
					CompressionEnabled = Compression,
					ThrowsExceptions = ThrowsExceptions
				};
			}
			catch (Exception e)
			{
				LogFactory.GetLogger().Exception("Error at create router.", e);

				if (watcher != null)
				{
					watcher.Dispose();
				}

				if (packRepository != null)
				{
					packRepository.Dispose();
				}

				throw;
			}
		}

		internal static WebPackerConfiguration Deserialize(XmlReader reader)
		{
			var context = GetSerializerContext();
			var serializer = new XmlSerializer<WebPackerConfiguration>(context, "web.packer", false);
			return serializer.Deserialize(reader);
		}

		internal static PackInfoCollection DeserializePackInfoCollection(string path)
		{
			using (var streamReader = new StreamReader(path))
			using (var xmlReader = XmlReader.Create(streamReader))
			{
				return DeserializePackInfoCollection(xmlReader);
			}
		}

		internal static PackInfoCollection DeserializePackInfoCollection(XmlReader reader)
		{
			var context = GetSerializerContext();
			var serializer = new XmlSerializer<PackInfoCollection>(context, "packs", false);
			return serializer.Deserialize(reader);
		}

		internal static XmlSerializerContext GetSerializerContext()
		{
			var context = new XmlSerializerContext();
			context.TypeResolver = new WebPackerTypeResolver();
			context.MappingProvider = new WebPackerMappingProvider();
			context.Converters.Add(new EncodingConverter());
			context.Serializers.Add(new PackInfoCollectionSerializer());
			return context;
		}
	}
}
